package com.baofu.paycore.service;

import com.baofu.clear.service.facade.enums.ClearStatusEnum;
import com.baofu.clear.service.facade.enums.PayTypeEnum;
import com.baofu.clear.service.facade.model.resp.NotifyDTO;
import com.baofu.paycore.biz.notice.ProductHandler;
import com.baofu.paycore.biz.paycore.handler.BaseHandler;
import com.baofu.paycore.biz.paycore.handler.impl.RefundHandler;
import com.baofu.paycore.biz.paycore.handler.impl.WithdrawHandler;
import com.baofu.paycore.biz.tools.BuildModel;
import com.baofu.paycore.common.enums.ErrorTypeEnum;
import com.baofu.paycore.common.enums.RedisKeyEnum;
import com.baofu.paycore.common.exception.PayCoreBizException;
import com.baofu.paycore.manager.model.PayBaseBO;
import com.baofu.paycore.service.facade.enums.PayCoreErrorCode;
import com.baofu.paycore.common.utils.ExceptionUtil;
import com.baofu.paycore.dal.model.ErrorLogDO;
import com.baofu.paycore.manager.cache.RedisLockManager;
import com.baofu.paycore.manager.model.PayBO;
import com.baofu.paycore.manager.model.RechargeBO;
import com.baofu.paycore.manager.storage.impl.ErrorLogManager;
import com.baofu.paycore.service.converter.BO2DTOConverter;
import com.baofu.paycore.service.facade.enums.PayStatusEnums;
import com.baofu.paycore.service.facade.enums.RequestSystemEnum;
import com.system.commons.exception.BizServiceException;
import com.system.commons.utils.ParamValidate;
import com.system.kafka.clients.handle.BizHandleInterface;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC;
import org.slf4j.helpers.SystemMarker;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * 清分回调
 * <p>
 * <p>
 * 1 启动消费消息
 * 2 业务处理
 * </p>
 * User: LZQ Date: 2016/05/06  Version: 1.0
 */
@Slf4j
@Service
public class CallBackService implements BizHandleInterface<NotifyDTO> {

    /**
     * 充值处理
     */
    @Resource(name = "webRechargeHandler")
    private BaseHandler<RechargeBO, NotifyDTO> rechargeHandler;

    /**
     * 消费服务
     */
    @Resource(name = "webConsumerHandler")
    private BaseHandler<PayBO, RechargeBO> consumeHandler;

    /**
     * 提现服务
     */
    @Resource(name = "withdrawHandler")
    private WithdrawHandler withdrawHandler;

    /**
     * 退款服务
     */
    @Autowired
    private RefundHandler refundHandler;

    /**
     * 发kafka消息服务
     */
    @Autowired
    private ProductHandler productHandler;

    /**
     * 锁
     */
    @Autowired
    private RedisLockManager redisLockManager;

    /**
     * 差错日志Manager
     */
    @Autowired
    private ErrorLogManager errorLogManager;

    /**
     * 业务处理
     *
     * @param callBackDTO 业务处理参数
     */
    @Override
    public void doBiz(NotifyDTO callBackDTO) {

        MDC.put(SystemMarker.TRACE_LOG_ID, callBackDTO.getTradeLogId());
        log.info("清分回调信息:{}", callBackDTO);

        String requestBizNo = callBackDTO.getRequestBizNo();
        String payType = callBackDTO.getPayType();
        boolean flag = false;
        try {
            ParamValidate.validateStringNull(requestBizNo, payType);
            //1、redis业务锁
            flag = redisLockManager.lock(RedisKeyEnum.CALL_BACK, requestBizNo, payType);

            //2、回调不是真实结果或初始化不作处理
            if (ClearStatusEnum.P.getCode().equals(callBackDTO.getStatus()) ||
                    ClearStatusEnum.I.getCode().equals(callBackDTO.getStatus())) {
                log.error("清分回调结果不是真实结果或清分单信息状态为初始化:{}", callBackDTO.getStatus());
                return;
            }

            //3、根据支付类型,确定回调业务处理具体实现
            PayTypeEnum payTypeEnum = PayTypeEnum.explain(callBackDTO.getPayType());
            if (null == payTypeEnum) {
                log.error("回调支付类型有误 payType:{}", callBackDTO.getPayType());
                throw new BizServiceException(PayCoreErrorCode.PAY_TYPE_NOT_EXISTED);
            }

            PayBaseBO domainModel;
            switch (payTypeEnum) {
                case PAY:
                    //针对代扣快捷业务,清分同步成功与异步kafka同时进行处理时,异步先执行,导致同步本该成功订单
                    //返回上层失败,故针对这种业务进行延时处理
                    Thread.sleep(600);

                    //处理充值单
                    RechargeBO rechargeBO = rechargeHandler.callBack(callBackDTO);
                    //推进支付单
                    domainModel = doConsume(rechargeBO);
                    break;

                case WITHDRAW:
                    domainModel = withdrawHandler.callBack(callBackDTO);
                    break;

                case REFUND:
                    domainModel = refundHandler.callBack(callBackDTO);
                    break;

                default:
                    log.error("暂不支持此业务：{}", payType);
                    throw new PayCoreBizException(PayCoreErrorCode.NOT_IN_SERVICE);
            }

            //4、回调后状态判断
            if (null == domainModel ||
                    (!PayStatusEnums.SUCCESS.getCode().equals(domainModel.getStatus()) &&
                            !PayStatusEnums.FAIL.getCode().equals(domainModel.getStatus()))) {
                return;
            }
            domainModel.setTradeLogId(callBackDTO.getTradeLogId());
            //5、通知交易
            productHandler.sendKafkaMsg(BO2DTOConverter.getPayMessageDTO(domainModel, callBackDTO));

        } catch (Exception e) {

            log.error("清分回调处理异常：{}", e);
            ErrorLogDO errorLogDO = BuildModel.buildErrorLog(callBackDTO.getRequestOrderNo(),
                    callBackDTO.getRequestBizNo(), ErrorTypeEnum.CLEAR_CALL_BACK_ERROR.getCode(),
                    payType, ExceptionUtil.doExceptionService(e).getErrorCode(),
                    ExceptionUtil.doExceptionService(e).getErrorMsg(), MDC.get(SystemMarker.TRACE_LOG_ID));
            errorLogManager.addOrder(errorLogDO);

        } finally {
            if (flag) {
                redisLockManager.release(RedisKeyEnum.CALL_BACK, requestBizNo, payType);
            }
            log.info("清分回调处理结束 {} 支付类型:{}", requestBizNo, payType);
        }
    }

    /**
     * 支付回调
     *
     * @param rechargeBO 充值订单
     * @return 处理结果
     */
    private PayBaseBO doConsume(RechargeBO rechargeBO) {
        if (!Objects.equals(RequestSystemEnum.PAYCORE.getCode(), rechargeBO.getRequestSystem()) ||
                Objects.equals(PayStatusEnums.INIT.getCode(), rechargeBO.getStatus())) {
            log.info("充值回调:{} 充值状态:{}", rechargeBO.getRequestSystem(), rechargeBO.getStatus());
            return rechargeBO;
        }
        //消费回调
        return consumeHandler.callBack(rechargeBO);
    }
}